package com.cc.lambda;

import org.junit.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * @Author C.
 * @Date 2019-03-30 23:36
 */
public class LambdaTest {

    @Test
    public void test_for_before_java8() {
        List<Integer> numbers = getIntegers();
        for (Integer n : numbers) {
            System.out.println(n);
        }
    }

    /**
     * Java8中引入了一个新的操作符“->” ,它将 Lambda表达式分为两个部分：
     * 在其左边：表示函数的参数
     * 在其右边：表示要执行的代码（称作Lambda体）
     */
    @Test
    public void test_for_in_java8_01() {
        List<Integer> numbers = getIntegers();
        numbers.forEach(x -> System.out.println(x));
    }

    @Test
    public void test_for_in_java8_02() {
        List<Integer> numbers = getIntegers();
        numbers.forEach(System.out::println);
    }

    @Test
    public void test_thread_before_java8() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("new thread before java8");
            }
        }).start();
    }

    /**
     * 所谓函数式接口，其实就是只包含一个抽象方法的接口。像Runnable接口，只有run方法，这就是典型的函数式接口
     * 在它的接口定义上，有那么一个注解@FunctionalInterface来表示这是一个函数式接口。
     * 这个@FunctionalInterface注解的作用就是当你定义了一个接口，
     * 但是这个接口不符合函数式接口的规定即只能有一个抽象方法，则编译时会报错。
     *
     * @FunctionalInterface
     */
    @Test
    public void test_thread_in_java8() {
        new Thread(() -> System.out.println("new thread in java8")).start();
    }


    /**
     * 有一个参数，并且无返回值
     * Consumer接口的accept方法就是一个有一个参数并且无返回值的方法，那么就可以这样子使用Lambda表达式来创建Consumer的实例
     */
    @Test
    public void test_lambda_with_arg_and_no_return() {
        Consumer<Integer> consumer = (x) -> System.out.println(x);
        consumer.accept(5);
    }

    /**
     * 语法变形
     * 其实，只需要一个参数的时候，”->”左边的括号可以省略
     */
    @Test
    public void test_lambda_with_arg_and_no_return_02() {
        Consumer<Integer> consumer = x -> System.out.println(x);
        consumer.accept(5);
    }

    /**
     * 当然直接使用方法引用也是可以的
     */
    @Test
    public void test_lambda_with_arg_and_no_return_03() {
        Consumer<Integer> consumer = System.out::println;
        consumer.accept(5);
    }

    /**
     * 有一个参数，并且有返回值
     * Function接口的apply方法就是一个有一个参数并且有返回值的方法，那么就可以这样子使用Lambda表达式来创建Function的实例
     * <p>
     * 其中，参数是String类型的，返回值是Integer类型的，
     * 下面代码的功能就是创建一个Function实例，它的apply方法功能是接受一个String类型的参数将其转换成Integer类型。
     * <p>
     * 需要注意的是，当”->”右边需要执行的代码有多条的时候，需要用大括号括起来。
     */
    @Test
    public void test_lambda_with_arg_and_with_return() {
        Function<String, Integer> fun = s -> {
            int result = Integer.parseInt(s);
            return result;
        };
        Integer num = fun.apply("2333");
        System.out.println(num);
    }

    /**
     * 语法变形
     * 当”->”右边需要执行的代码只有一条语句，return和大括号都可以省略不写
     */
    @Test
    public void test_lambda_with_arg_and_with_return_02() {
        Function<String, Integer> f = s -> Integer.parseInt(s);
        Integer num = f.apply("2333");
        System.out.println(num);
    }

    /**
     * 语法变形
     * 使用方法引用
     */
    @Test
    public void test_lambda_with_arg_and_with_return_03() {
        Function<String, Integer> f = Integer::parseInt;
        Integer num = f.apply("2333");
        System.out.println(num);
    }

    /**
     * 多参数的情况
     * BiFunction接口的apply方法就是一个有两个参数并且有返回值的方法
     *
     * 这段代码的功能是创建一个BiFunction接口实例，其apply方法的功能是接收两个Integer类型的参数让它们相加然后转成String返回
     */
    @Test
    public void test_lambda_with_args_and_with_return() {
        BiFunction<Integer, Integer, String> b = (x, y) -> x + y + "";
        String str = b.apply(1, 2);
        System.out.println(str);
    }

    /**
     * 在”->”左边的参数都没有声明它们的类型，那其实是可以声明的
     * 不过我们一般都不去声明，麻烦。反正编译器很聪明，可以帮我们推断出参数类型。
     * 以前最经常见到的类型推断不就这样子的嘛List<String> list = new ArrayList<>();
     * ArrayList的尖括号不用填类型，编译器也能推断出来是String
     */
    @Test
    public void test_lambda_with_args_and_with_return_02() {
        BiFunction<Integer, Integer, String> b = (Integer x, Integer y) -> x + y + "";
        String str = b.apply(1, 2);
        System.out.println(str);
    }

    private List<Integer> getIntegers() {
        List<Integer> numbers = new ArrayList<>();
        numbers.add(1);
        numbers.add(2);
        numbers.add(3);
        return numbers;
    }
}
